//
//  HomeTableViewController.swift
//  weibo
//
//  Created by 张衡 on 15/7/29.
//  Copyright © 2015年 张衡. All rights reserved.
//

/**
注册通知接收，接收collecttion cell点击后发出的通知
*/
import UIKit

class HomeTableViewController: baseTableViewController {
   
    /// 微博数据数组
    var statuses: [Status]? {
        didSet {
            // 刷新数据
            tableView.reloadData() // 系统方法
        }
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 如果没有登录，设置访客视图，返回, 在统一的view的基础上，进行函数调用，得到自己特有的view
        if !UserAccount.userLogon {
            
            visitorView?.setupViewInfo(true, imageName: "visitordiscover_feed_image_smallicon", message: "关注一些人，回这里看看有什么惊喜")
            
            return
        }
        
        // 已登录
        prepareTableView()
        loadData()
        
        // 注册通知
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "selectPicture:", name: ZHStatusCellSelectPictureNotification, object: nil)
    }
    
    deinit {
        // 注销通知
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    
    // 接收到通知触发的方法
    @objc private func selectPicture(n: NSNotification) {
        // 获取参数，提示：从自定义通知获取参数，一定记住检查参数值
        // urls
        guard let urls = n.userInfo![ZHStatusCellSelectPictureURLKey] as? [NSURL] else {
            print("URL数组为空")
            return
        }
        
        // indexpath
        guard let indexPath = n.userInfo![ZHStatusCellSelectPictureIndexKey] as? NSIndexPath else {
            print("indexpath 不存在")
            return
        }
        
        // 检查图像视图
        guard let picView = n.object as? StatusPictureView else {
            print("图像视图不存在")
            return
        }
        
        // 导出主页中的图片视图（collectionview)
        pictureView = picView
        
        // 金蝉脱壳 UIImageView = 设置转场动画的 iconView
        presentIconView.sd_setImageWithURL(urls[indexPath.item])
        
        // 导出起始图片索引
        presentIndexPath = indexPath
        
        // 提示：传递给照片查看器的照片索引只是一个整数，适用面会更强
        let vc = PhotoBrowserViewController(urls: urls, Index: indexPath.item)
        //print("\(urls) --- \(indexPath.item)")
        
       
        // 自定义“转场”
        // 系统modal“转场”完成之后，之前的view会被移除屏幕, 自定义却不会移除，好处，可以依托之前的视图，来做一些动画（从某个地方开始，前往某个地方）
        // 1. 指定代理
        vc.transitioningDelegate = self
        //weak var transitioningDelegate: UIViewControllerTransitioningDelegate?
        
        // 2. 指定modal 转场模式 - 自定义
        vc.modalPresentationStyle = UIModalPresentationStyle.Custom
        
        presentViewController(vc, animated: true, completion: nil)
        
        
        
    }
    
    /// 准备表格视图
    private func prepareTableView() {
        
        // 注册原型 cell --- swift要求 特有的
        // rawValue 返回枚举对应实际数值 ---------- 通过可重用的ID 来选择不同的注册类型
        tableView.registerClass(StatusNomalCell.self, forCellReuseIdentifier: StatusCellIdentifier.NormalCell.rawValue)
        
        tableView.registerClass(StatusForwardCell.self, forCellReuseIdentifier: StatusCellIdentifier.ForwardCell.rawValue)
        
        // 设置表格的预估行高(方便表格提前计算预估行高，提高性能) - 尽量准确，能够提高性能
        // 能够减少调用行高的次数
        tableView.estimatedRowHeight = 300
        
//        // 设置表格自动计算行高 （ cell顶部到底部有确定的线性约束时使用）
//        tableView.rowHeight = UITableViewAutomaticDimension
        // 取消分割线
        tableView.separatorStyle = UITableViewCellSeparatorStyle.None
        
        tableView.showsVerticalScrollIndicator = false
        
        
        // 指定刷新控件
        refreshControl = ZHRefreshControl()
        // 控件触发刷新的方法
        refreshControl?.addTarget(self, action: "loadData", forControlEvents: UIControlEvents.ValueChanged)
        
        
    }
    
    
    /// 上拉刷新的标记
    private var pullupRefreshFlag = false
    
    
    
    /// 加载数据 (默认加载下拉刷新数据)
    func loadData() {
        
        // 开始动画 - 只是开启了刷新控件的动画，并不会触发加载数据的方法
        self.refreshControl?.beginRefreshing()  // 系统方法，平常都是手动触发
        
        // 刷新数据，获取到数组中第一条微博的 ID
        // 第一次执行此方法 statuses 为空，所以 加？？ 0, 加载最新20条
        var since_id = statuses?.first?.id ?? 0
 
        var max_id = 0
        
        // 判断是否是上拉刷新
        if pullupRefreshFlag {
            // 清零
            since_id = 0
            
            // 记录数组最后一条记录的 id
            max_id = statuses?.last?.id ?? 0
        }
        
        Status.loadStatus(since_id, max_id: max_id) { (dataList, error) -> () in      // 尾闭   删除了 [weak self]
            
            // 加载完成之后，关闭刷新控件
            self.refreshControl?.endRefreshing()
            
            // 复位上拉刷新标记，保证下一次仍然可上拉
            self.pullupRefreshFlag = false
            
            if error != nil {
                print(error)
                return
            }
            
            let count = dataList?.count ?? 0
            // print("刷新到 \(count) 条数据")
            
            
            // 只有下拉刷新才需要提示
            if since_id > 0 {
                self.showPulldownTip(count)
            }
            
            
            // 如果没有数据，直接返回
            if count == 0 {
                return
            }
            
            if since_id > 0 {       // 做下拉刷新，应该将结果集合放在之前数组的前面
                self.statuses = dataList! + self.statuses!  // 触发刷新
            } else if max_id > 0 {  // 上拉刷新，放后面
                self.statuses! += dataList!
                
                
            } else {
                self.statuses = dataList
            }
        } // 尾闭
        
    }
    
    
    /// 显示下拉刷新的数据条数
    private func showPulldownTip(count: Int) {
        
        // 提问：UIView 的动画(块代码动画）是如何实现的？是对核心动画的封装，添加到 控件的 layer 上
        // 块代码动画中，修改 frame 本质上是修改 position
        // 判断动画是否正在执行
        print(tipLabel.layer.animationKeys())
        
        if tipLabel.layer.animationForKey("position") != nil {
            
            // 动画正在执行
            return
        }
        
        
        // 提示： 如果频繁下拉，下一次动画的起点，是上一次动画的目标终点，这会造成提示栏不断向下
        
        
        tipLabel.text = (count == 0) ? "暂时没有新微博" : "刷新到 \(count) 条微博"
        
        // 标签初始位置
        let rect = tipLabel.frame
        
        UIView.animateWithDuration(1.5, animations: { () -> Void in
            
            // UIView.setAnimationCurve(UIViewAnimationCurve(rawValue: 7)!)
            // 自动反转动画
            UIView.setAnimationRepeatAutoreverses(true)
            
            // 移动 label 的位置
            self.tipLabel.frame = CGRectOffset(rect, 0, 3 * rect.height)
            
            }) { (_) -> Void in
                
                self.tipLabel.frame = rect
        }
        
        // 测试 动画时长
        let anim = tipLabel.layer.animationForKey("position")
        print("动画时长\(anim?.duration)")
    }

    
    
//    /// 显示下拉刷新的数据条数
//    private func showPulldownTip(count: Int) {
//        // 定义标签
//        let h: CGFloat = 44
//        let label = UILabel(frame: CGRect(x: 0, y: -2 * h, width: view.bounds.width, height: h))
//        label.backgroundColor = UIColor.orangeColor()
//        label.textColor = UIColor.whiteColor()
//        label.textAlignment = NSTextAlignment.Center
//        label.text = "刷新到 \(count) 条微博"
//        
//        // 将 label 添加到界面(view不合适，会一起滚动)
//        // 加载 navBar 上面，不会随着 tableView 一起滚动
//        navigationController?.navigationBar.insertSubview(label, atIndex: 0)
//        
//        UIView.animateWithDuration(2.0, animations: { () -> Void in
//            // 自动反转动画
//            UIView.setAnimationRepeatAutoreverses(true)
//            // 移动 label 的位置
//            label.frame = CGRectOffset(label.frame, 0, 3 * h)
//            }) { (_) -> Void in
//                label.removeFromSuperview()
//        }
//        
//        // UIView.animateWithDuration(<#T##duration: NSTimeInterval##NSTimeInterval#>, animations: <#T##() -> Void#>, completion: <#T##((Bool) -> Void)?##((Bool) -> Void)?##(Bool) -> Void#>)
//    }
    
    
    
    // MARK: - 数据源方法
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return statuses?.count ?? 0
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
        // swift 特有的方法，要求必须注册原型cell, storyboard，register Class
        // 根据微博类型，选取不同的ID
        let status = statuses![indexPath.row]
        
        let cell = tableView.dequeueReusableCellWithIdentifier(StatusCellIdentifier.cellId(status), forIndexPath: indexPath) as! StatusCell
        
        
        // 判断 indexPath 的数值
        if indexPath.row == statuses!.count - 1 {
            // 设置上拉刷新标记
            pullupRefreshFlag = true
            // 刷新数据
            loadData()
        }
        
        
        // 这个方法，和OC加载可重用方法一致，不需要注册原型cell
        // let cell = tableView.dequeueReusableCellWithIdentifier(<#T##identifier: String##String#>)
        
        cell.status = status
        
        return cell
    }
    
    /// 返回行高 － 如果是固定值，可以直接通过属性设置，效率更高
    /**
    行高缓存
    1. NSCache - 内存缓存，iOS 8.0 有 bug，千万不要 removeAll，一旦 removeAll 之后，再也没法使用了
    SDWebImage 存在什么问题
    1> 接收到内存警告后，内存缓存实效
    2> 加载 GIF 的时候，内存会狂飙！
    2. 自定义行高`缓存字典`
    3. 直接利用微博的`模型` - 行高数据只是一个小数，并不会有太大的内存消耗！
    不需要在控制器中，额外定义属性！
    
    提示：如果表格高度是固定的，一定不要通过代理返回行高！可以直接设置属性！
    
    
    
    
    
    表格 & 数据源执行的顺序
    有预估行高
    1> 行数，知道多少行，根据预估行高，计算整个 tableView 的预估 contentSize，保证能够滚动
    
    2> 数据源 cell，获取要显示的 cell，会直接调用行高方法，返回当前行的高度
    3> 行高
    
    2.3会重复调用，一直到超出当前屏幕位置
    contentSize 会有一个变化，显示的行会用计算好的行高，没有显示的行，仍然用预估行高
    如果都滚动完成，contentSize 才是一个准确的值
    
    好处，提前计算的数据量小
    缺点，滚动过程中，临时计算行高，如果行高计算过于复杂，会影响性能！
    
    没有预估行高
    1> 行数，知道有多少行
    2> 行高，计算每一行的行高，累加，就能够得到 contentSize 的 height
    3> 数据源 cell，当 cell 显示的时候，整个 tableView 的 contentSize 已经计算完毕，能够保证快速滚动！
    
    重点：UITableView 是继承自 UIScrollView，滚动依赖于 `contentSize`
    
    tableView.cellForRowAtIndexPath(indexPath) 通常是在表格的其他代理方法，例如：选中行中根据索引获取 cell
    注意：调用的时候，cell一定已经被创建出来！，不能在数据源方法中调用
    
    */
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        
        // 判断模型中，是否已经缓存了行高
        // 1. 获取模型
        let status = statuses![indexPath.row]
        
        if let h = status.rowHeight {
            return h
        }
        
        // 其他位置获取 cell 的方法，但是本函数中，无法使用此方法获取到 cell
        //        let c = tableView.cellForRowAtIndexPath(indexPath)
        
        
        // 2. 获取 cell - dequeueReusableCellWithIdentifier 带 indexPath 的函数会调用计算行高的方法
        // 会造成死循环，在不同版本的 Xcode 中 行高的计算次数不一样！尽量要优化！
        // 如果不做处理，会非常消耗性能！
        let cell = tableView.dequeueReusableCellWithIdentifier(StatusCellIdentifier.cellId(status)) as? StatusCell
        
        // 3. 记录并返回计算的行高
        status.rowHeight = cell!.rowHeight(status)
        
        return status.rowHeight!
    }
    
    
    private lazy var tipLabel: UILabel = {
        
        // 定义标签
        let h: CGFloat = 44
        let label = UILabel(frame: CGRect(x: 0, y: -2 * h, width: self.view.bounds.width, height: h))
        label.backgroundColor = UIColor.orangeColor()
        label.textColor = UIColor.whiteColor()
        label.textAlignment = NSTextAlignment.Center
        
        // 将 label 添加到界面(view不合适，会一起滚动)
        // 加载 navBar 上面，不会随着 tableView 一起滚动
        self.navigationController?.navigationBar.insertSubview(label, atIndex: 0)
        
        return label
        }()
    
    /// 是否是 modal 展现的标记
    private lazy var isPresented = false
    
    /// 动画转场过程中的图像视图
    private lazy var presentIconView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = UIViewContentMode.ScaleAspectFill
        iv.clipsToBounds = true
        return iv
    }()
    
    /// 转场选中的图片 collectionView， 可以计算起始位置&目标位置（调用函数）
    private var pictureView: StatusPictureView?
    
    /// 转场起始的图片索引
    private var presentIndexPath: NSIndexPath?
    
}

// 自定义转场协议
extension HomeTableViewController: UIViewControllerTransitioningDelegate {
    /// 返回提供转场的对象（遵守 `UIViewControllerAnimatedTransitioning` 协议）
    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        
        isPresented = true
        return self
    }
    
    /// 返回提供解除转场的对象
    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        isPresented = false
        
        return self
    }
}

// 自定义转场动画                    // 动画过度的协议
extension HomeTableViewController: UIViewControllerAnimatedTransitioning {
    
    // 转场动画时长
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
       // transitionContext!.containerView()?.backgroundColor = UIColor.blackColor()
        return 2.0
    }
    
    // 自定义转场动画 - 只要实现了此方法，就需要自己来写动画代码
    /**
    transitionContext : 提供了转场动画需要的元素
    
    // 动画结束之后，一定要执行，如果不执行，系统会一直等待，无法进行后续交互
    transitionContext.completeTransition(true)

    要modal展现出来的界面，装在容器视图中
    */
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        
        // 这两个方法可以拿到，来控制器和去控制器，与toView，，，fromView 类似
//        let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
//        let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
        
        if isPresented {
            

            // 最终要展现在界面上的 view，(PhotoBrowserViewController 的 view） 在动画结束之前不需要显示
            let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
            // print(toView) 是 PhotoBrowserViewController 的 view ，
            
            // 将要展现视图，添加到容器视图中
            transitionContext.containerView()?.addSubview(presentIconView)
            
            // 指定图像视图的起始位置
            let fromRect = pictureView!.cellScreenFrame(presentIndexPath!)
            let toRect = pictureView!.cellFullScreenFrame(presentIndexPath!)
            
            // 设置起始位置
            presentIconView.frame = fromRect
            // toView.alpha = 0
            
            // 调用函数获取时长
            UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
                //toView.alpha = 1.0
                self.presentIconView.frame = toRect
                
                }, completion: { (_) -> Void in
                    
                    
                    // 添加目标视图
                    transitionContext.containerView()?.addSubview(toView)
                    
                    // 删除动画的图像视图
                    self.presentIconView.removeFromSuperview()
                    
                    // 动画结束之后，一定要执行，如果不执行，系统会一直等待，无法进行后续交互
                    transitionContext.completeTransition(true)
            })
            
            return
        }
        
// 尽可能的减少，程序的判断执行，
        
        // 解除转场
        // 照片查看器的控制器
        let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! PhotoBrowserViewController
        // 从控制器中获取当前显示的照片索引
        let indexPath = fromVC.currentImageIndex() // 可视的那一个
        
        // 获取到微博 cell 中 collection cell 对应的位置,原始位置
        let rect = pictureView?.cellScreenFrame(indexPath)
        
        // 获取当前的图像视图
        let iv = fromVC.currentImageView() // cell 中的 imageView
        
        // 照片视图
        let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
        // print(fromView)
        // 设置图像视图位置
        iv.center = fromView.center
        
        // 累加 父视图的形变
        let scale = fromView.transform.a // 在浏览器中的代理方法中修改的
        iv.transform = CGAffineTransformScale(iv.transform, scale, scale) // 别人触发这个方法，触发之后再修改
        
        // 将当前图像视图添加到容器视图
        transitionContext.containerView()?.addSubview(iv)
        
        // 将fromView 从容器视图中移除
        fromView.removeFromSuperview()
        UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
            iv.frame = rect!
            
            }) { (_) -> Void in
                
                iv.removeFromSuperview()
                // 解除转场时，会把 容器视图以及内部的内容一起销毁
                transitionContext.completeTransition(true)
        }
    }
    
}












